package rad.framework.mda.jpagen.actions;

import java.util.Iterator;

import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.ui.navigator.CommonActionProvider;
import org.eclipse.ui.navigator.ICommonActionExtensionSite;
import org.eclipse.ui.navigator.ICommonMenuConstants;
import org.eclipse.uml2.common.edit.command.ChangeCommand;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Type;

/**
 * 
 * @author Robert
 * 
 * Provide an entity with a primary key id
 * having the standard stereotypes @Id and
 * @GeneratedValue. 
 */
public class AddPrimaryKeyActionExtension extends CommonActionProvider implements
		IMenuListener {

	private ICommonActionExtensionSite aConfig;
		
	@Override
	public void init(ICommonActionExtensionSite aConfig) {
	    this.aConfig = aConfig;
	}

	public void fillContextMenu(IMenuManager aMenu) {
		aMenu.appendToGroup(ICommonMenuConstants.GROUP_NEW,
				new PrimaryKeyAction());
	}

	private void addPrimaryKey() {
		Type longType = getLongType();
		Property property = getEntity().createOwnedAttribute("id", longType, 1, 1);
		applyIdStereotype(property);
		applyGeneratedValueStereotype(property);
		addComment(property, "The primary key.");
	}

	private void applyGeneratedValueStereotype(Property property) {
		for (Iterator it = property.getApplicableStereotypes().iterator(); it.hasNext();) {
			Stereotype stereotype = (Stereotype) it.next();
			if (stereotype.getName().equals("javax.persistence.GeneratedValue")) {
				property.applyStereotype(stereotype);
			}			
		}		
	}

	private void applyIdStereotype(Property property) {
		for (Iterator it = property.getApplicableStereotypes().iterator(); it.hasNext();) {
			Stereotype stereotype = (Stereotype) it.next();
			if (stereotype.getName().equals("javax.persistence.Id")) {
				property.applyStereotype(stereotype);
			}			
		}	
	}

	private void addComment(Element element, String comment) {
		Comment newcomment = element.createOwnedComment();
		newcomment.setBody(comment);
		
	}
	
	private Type getLongType() {
		Package pkg = getEntity().getModel();
		Profile types = pkg.getAppliedProfile("types");
		return types.getOwnedType("Long");
	}

	private Class getEntity() {
		return (Class) getSelection().getFirstElement();
	}

	public void menuAboutToShow(IMenuManager manager) {
	}

	private class PrimaryKeyAction extends Action {
		private static final String LABEL = "Add Primary Key";

		public PrimaryKeyAction() {
			setText(LABEL);
			setToolTipText(getText() + " Tip");
			setDescription(getText() + " Description");
		}
		
		public void run() {
			// needed; otherwise an EMF transaction error occurs.
			getEditingDomain().getCommandStack().execute(
					new RefreshingChangeCommand(getEditingDomain(),
							new Runnable() {
						public void run() {
							addPrimaryKey();
						}
					}, LABEL)
					);
		}
		private TransactionalEditingDomain getEditingDomain() {
			return TransactionUtil.getEditingDomain(getEntity());
		}
	}
	
	private class RefreshingChangeCommand extends ChangeCommand {
		public RefreshingChangeCommand(EditingDomain editingDomain, Runnable runnable, String label) {
			super(editingDomain, runnable, label);
		}
		
		public void execute() {
			super.execute();
			getViewer().refresh();
		}
		
		private TreeViewer getViewer() {
			return (TreeViewer) aConfig.getStructuredViewer();
		}

		public void undo() {
			super.undo();
			getViewer().refresh();
		}
		
		public void redo() {
			super.redo();
			getViewer().refresh();
		}

	}	


	private IStructuredSelection getSelection() {
		return (IStructuredSelection) getContext().getSelection();
	}	
}
